<%#
 Copyright 2013-2025 the original author or authors from the JHipster project.

 This file is part of the JHipster project, see https://www.jhipster.tech/
 for more information.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      https://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-%>
import axios, { AxiosResponse } from 'axios';
<%_ if (authenticationTypeJwt || enableTranslation) { _%>
import { Storage } from 'react-jhipster';
<%_ } _%>
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { serializeAxiosError } from './reducer.utils';

import { AppThunk } from 'app/config/store';
<% if (enableTranslation) { %>import { setLocale } from 'app/shared/reducers/locale';<% } %>

<%_ if (authenticationTypeJwt) { _%>
const AUTH_TOKEN_KEY = '<%= jhiPrefixDashed %>-authenticationToken';
<%_ } _%>

export const initialState = {
  loading: false,
  isAuthenticated: false,
<%_ if (!authenticationTypeOauth2) { _%>
  loginSuccess: false,
  loginError: false, // Errors returned from server side
  showModalLogin: false,
<%_ } _%>
  account: {} as any,
  errorMessage: null as unknown as string, // Errors returned from server side
  redirectMessage: null as unknown as string,
  sessionHasBeenFetched: false,
  logoutUrl: null as unknown as string,
};

export type AuthenticationState = Readonly<typeof initialState>;

// Actions

export const getSession = (): AppThunk => <% if (enableTranslation) { %>async<% } %> (dispatch, getState) => {
<%_ if (enableTranslation) { _%>
  await dispatch(getAccount());

  const { account } = getState().authentication;
  if (account?.langKey) {
    const langKey = Storage.session.get('locale', account.langKey);
    await dispatch(setLocale(langKey));
  }
<%_ } else { _%>
  dispatch(getAccount());
<%_ } _%>
};

export const getAccount = createAsyncThunk('authentication/get_account', async () => axios.get<any>('api/account'), {
  serializeError: serializeAxiosError,
});

<%_ if (authenticationTypeSession) { _%>
export const authenticate = createAsyncThunk('authentication/login', async (data: string) => axios.post<any>('api/authentication', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }), {
  serializeError: serializeAxiosError,
});

export const login: (username: string, password: string, rememberMe?: boolean) => AppThunk =
  (username, password, rememberMe = false) =>
  async dispatch => {
    const data = `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&remember-me=${rememberMe}&submit=Login`;
    await dispatch(authenticate(data));
    dispatch(getSession());
  };
<%_ } else if (authenticationTypeJwt) { _%>
interface IAuthParams {
  username: string;
  password: string;
  rememberMe?: boolean;
}

export const authenticate = createAsyncThunk(
  'authentication/login',
  async (auth: IAuthParams) => axios.post<any>('api/authenticate', auth),
  {
    serializeError: serializeAxiosError,
  },
);

export const login: (username: string, password: string, rememberMe?: boolean) => AppThunk =
  (username, password, rememberMe = false) =>
  async dispatch => {
    const result = await dispatch(authenticate({ username, password, rememberMe }));
    const response = result.payload as AxiosResponse;
    const bearerToken = response?.headers?.authorization;
    if (bearerToken?.startsWith('Bearer ')) {
      const jwt = bearerToken.slice(7, bearerToken.length);
      if (rememberMe) {
        Storage.local.set(AUTH_TOKEN_KEY, jwt);
      } else {
        Storage.session.set(AUTH_TOKEN_KEY, jwt);
      }
    }
    dispatch(getSession());
  };
<%_ } _%>

<%_ if (authenticationTypeJwt) { _%>
export const clearAuthToken = () => {
  if (Storage.local.get(AUTH_TOKEN_KEY)) {
    Storage.local.remove(AUTH_TOKEN_KEY);
  }
  if (Storage.session.get(AUTH_TOKEN_KEY)) {
    Storage.session.remove(AUTH_TOKEN_KEY);
  }
};

export const logout: () => AppThunk = () => dispatch => {
  clearAuthToken();
  dispatch(logoutSession());
};
<%_ } else { _%>
export const logoutServer = createAsyncThunk('authentication/logout', async () => axios.post<any>('api/logout', {}), {
  serializeError: serializeAxiosError,
});

export const logout: () => AppThunk = () => async dispatch => {
  await dispatch(logoutServer());
  // fetch new csrf token
  dispatch(getSession());
};
<%_ } _%>

export const clearAuthentication = messageKey => dispatch => {
<%_ if (authenticationTypeJwt) { _%>
  clearAuthToken();
<%_ } _%>
  dispatch(authError(messageKey));
  dispatch(clearAuth());
};

export const AuthenticationSlice = createSlice({
  name: 'authentication',
  initialState: initialState as AuthenticationState,
  reducers: {
    <%_ if (authenticationTypeJwt) { _%>
    logoutSession() {
      return {
        ...initialState,
        showModalLogin: true,
      };
    },
    <%_ } _%>
    authError(state, action) {
      return {
        ...state,
<%_ if (!authenticationTypeOauth2) { _%>
        showModalLogin: true,
<%_ } _%>
        redirectMessage: action.payload
      };
    },
    clearAuth(state) {
      return {
        ...state,
        loading: false,
<%_ if (!authenticationTypeOauth2) { _%>
        showModalLogin: true,
<%_ } _%>
        isAuthenticated: false
      };
    },
  },
  extraReducers(builder) {
    builder
      <%_ if (!authenticationTypeOauth2) { _%>
      .addCase(authenticate.rejected, (state, action) => ({
        ...initialState,
        errorMessage: action.error.message,
        showModalLogin: true,
        loginError: true,
      }))
      .addCase(authenticate.fulfilled, state => ({
        ...state,
        loading: false,
        loginError: false,
        showModalLogin: false,
        loginSuccess: true,
      }))
      <%_ } _%>
      .addCase(getAccount.rejected, (state, action) => ({
        ...state,
        loading: false,
        isAuthenticated: false,
        sessionHasBeenFetched: true,
        <%_ if (!authenticationTypeOauth2) { _%>
        showModalLogin: true,
        <%_ } _%>
        errorMessage: action.error.message,
      }))
      .addCase(getAccount.fulfilled, (state, action) => {
        const isAuthenticated = action.payload?.data?.activated;
        return {
          ...state,
          isAuthenticated,
          loading: false,
          sessionHasBeenFetched: true,
          account: action.payload.data,
        };
      })
      <%_ if (!authenticationTypeJwt) { _%>
      .addCase(logoutServer.fulfilled, (state<% if (authenticationTypeOauth2) { %>, action<% } %>) => ({
        ...initialState,
        <%_ if (!authenticationTypeOauth2) { _%>
        showModalLogin: true
        <%_ } else { _%>
        logoutUrl: action.payload.data.logoutUrl
        <%_ } _%>
      }))
      <%_ } _%>
      <%_ if (!authenticationTypeOauth2) { _%>
      .addCase(authenticate.pending, state => {
        state.loading = true;
      })
      <%_ } _%>
      .addCase(getAccount.pending, state => {
        state.loading = true;
      });
  },
});

export const { <% if (authenticationTypeJwt) { %>logoutSession,<% } %> authError, clearAuth } = AuthenticationSlice.actions;

// Reducer
export default AuthenticationSlice.reducer;
